package com.zzy.furns.utils;

import com.google.gson.Gson;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.Map;

import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_DATE;
import static com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY;
@SuppressWarnings({"all"})
public class DataUtils {

    private static final Object STOCK = new Object();

    public static <T> T copyParamToBean(T bean, Map<String,String[]> map){
        try {
            BeanUtils.populate(bean,map);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return bean;
    }

    public static Integer parseInt(String str){
        Integer defaultNum = -1;
        try {
            Integer num = Integer.parseInt(str);
            return num;
        } catch (NumberFormatException e) {
            return defaultNum;
        }
    }

    /**
     * 返回数组中第一个元素为起始索引 第二个元素为总页数
     */
    public static Integer[] getBeginIndexAndPageTotal(Integer totalRow,Integer pageSize,Integer pageNo){
        //进行非空、非0判断，增强程序健壮性
        try {
            //计算起始索引
            int begin = (pageNo - 1) * pageSize;
            int pageTotal = (int) Math.ceil((double) totalRow / pageSize);
            return new Integer[]{begin,pageTotal};
        } catch (Exception e) {
            System.out.println("起始索引或总页数计算异常");
            return null;
        }
    }

    /**
     * 校验验证码的信息与有效性，并给出相应的提示信息
     */
    public static Boolean checkCode(HttpServletRequest req){
        String code = req.getParameter("code");

        //获取Session中存储的验证码信息并立即删除
        /**
         * 立即清除该条属性
         *   是否需要考虑线程安全问题？
         *      比如说同一个浏览器瞬时有两个请求到达，第一个请求获取到kaptchaSessionKey后还没有执行remove操作
         *      第二个请求已经执行获取到了kaptchaSessionKey，此时就会造成一个验证码注册了两个用户，存在恶意注册
         *      漏洞
         *      如果用户名相同，因为数据库中用户名字段做了唯一约束，所以数据库会处理并发情况，但是这也可能会导致insert
         *      语句的大量执行，从而占用数据库资源
         *
         */
        synchronized (STOCK) {
            Object kaptchaSessionKey = req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
            req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);

            //校验验证码内容(不忽略大小写)
            /**
             * 1.验证KaptchaSessionKey 是否实现了自动删除 否
             * 2.会出现System.out.println(kaptchaSessionKey);结果为null的原因
             *      用户名已存在，请求转发回login.jsp页面，发起获取验证码申请，同时将验证码信息保存在Session中
             *      此时重新部署，删除Session，导致结果为null
             *      即当页面处于login.jsp时，就已经发起了获取验证码的申请，同时将验证码信息保存在Session中了
             *      此时重新部署，删除Session，导致结果为null
             * 3.验证思路
             *      1.重新部署后，点击验证码进行刷新，重新发起获取验证码的申请
             *      2.获取SessionId 则应该输出两个不一样的SessionId
             * 4.考虑线程安全，在高并发场景下会出现一个验证码实现多个用户的注册（已使用Apifox测试观察到）
             */
            if (kaptchaSessionKey == null || !kaptchaSessionKey.equals(code)) {
                req.setAttribute("registerErrorMsg", "验证码输入错误");
                return false;
            }
        }

        //校验验证码是否有效
        Date kaptchaSessionDate = (Date)req.getSession().getAttribute(KAPTCHA_SESSION_DATE);
        if (new Date().getTime() - kaptchaSessionDate.getTime() > 1000 * 60){
            req.setAttribute("registerErrorMsg","验证码失效");
            return false;
        }

        return true;
    }

    /**
     * 返回JSON数据给Ajax引擎
     */
    public static void sendJSON(Map map, HttpServletResponse resp) throws IOException {
        Gson gson = new Gson();
        String json = gson.toJson(map);
        PrintWriter writer = resp.getWriter();
        writer.write(json);
        writer.flush();
        writer.close();
    }

}
